package gecko.impl;

import java.io.IOException;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @author 陈永佳 (yoojiachen@gmail.com)
 * @version 0.0.1
 */
public class UdpOutputStream extends OutputStream {

    private final DatagramSocket mSocket;
    private final byte[] mBuffer;
    private int mIndex = 0; // buffer index; points to next empty buffer byte

    public UdpOutputStream(DatagramSocket socket, int bufferSize) {
        mSocket = socket;
        mBuffer = new byte[bufferSize];
    }

    @Override
    public void close() throws IOException {
        mIndex = 0;
    }

    @Override
    public void flush() throws IOException {
        if (mIndex == 0) {  // no data in buffer
            return;
        }
        // copy what we have in the buffer so far into a new array;
        // if buffer is full, use it directly.
        final byte[] data;
        if (mIndex == mBuffer.length) {
            data = mBuffer;
        } else {
            data = new byte[mIndex];
            System.arraycopy(mBuffer,
                    0,
                    data,
                    0,
                    mIndex);
        }
        // send data
        DatagramPacket packet = new DatagramPacket(data, mIndex);
        mSocket.send(packet);
        // reset buffer index
        mIndex = 0;
    }

    @Override
    public void write(int value) throws IOException {
        mBuffer[mIndex] = (byte) (value & 0x0ff);
        mIndex++;

        if (mIndex >= mBuffer.length) {
            flush();
        }
    }

    @Override
    public void write(byte[] data) throws IOException {
        write(data, 0, data.length);
    }

    @Override
    public void write(byte[] data, int off, int len) throws IOException {
        int lenRemaining = len;
        try {
            while (mBuffer.length - mIndex <= lenRemaining) {
                System.arraycopy(data,
                        off + (len - lenRemaining),
                        mBuffer,
                        mIndex,
                        mBuffer.length - mIndex);
                lenRemaining -= mBuffer.length - mIndex;
                mIndex = mBuffer.length;
                flush();
            }

            if (lenRemaining == 0) {
                return;
            }

            System.arraycopy(data,
                    off + (len - lenRemaining),
                    mBuffer,
                    mIndex,
                    lenRemaining);
            mIndex += lenRemaining;
        } catch (ArrayIndexOutOfBoundsException e) {
            System.err.println("len: " + len);
            System.err.println("lenRemaining: " + lenRemaining);
            System.err.println("mIndex: " + mIndex);
            System.err.println("buffer.length: " + mBuffer.length);
            System.err.println("offset: " + off);
            System.err.println("data.length: " + data.length);
            throw e;
        }
    }

}
